/* NOTES TO REMEMBER
 * Could add
 * - hitboxes to all objects to make collision better
 * - levels
 * - bosses
 * - explosions / particles
 * - parallax background
 * - vectors for movement
 * - lirbraries! http://www.createjs.com/#!/CreateJS
 */

/* RESOURCES
 * http://www.w3schools.com/html5/html5_ref_av_dom.asp
 * http://www.superflashbros.net/as3sfxr/
 */

//data cross games
var totalScoreFromAllGames = 0;
var totalGames = 4;

var curStrategy;
var strategies;
/**
 * Initialize the Game and start it.
 */

var isDebugMode = true;

var game = new Game();

function setGameType() {
    console.log("isPreviewGame: ", window.isPreviewGame);
	if (typeof (window.currentGame) == "undefined") {
		window.currentGame = 1;
	} else {
		window.currentGame++;
	}

	if (window.isPreviewGame == true) {
		curStrategy = previewStrategy;
		totalGames = 1;
	} else {
        if (strategies == undefined) {
			strategies = createStrategies();
		}
		curStrategy = strategies[window.currentGame - 1];
	}

	getNewScenario().setGameLength(curStrategy.gameLength);
	
	_trialController = getTrialController(window.isPreviewGame);
	setTrialController(_trialController);

	console.log("Strategy name: " + curStrategy.name + ", game number: " + window.currentGame);
	console.log("game length: " + curStrategy.gameLength);
	getNewScenario().init();

	if (window.testGameLength) {
	    getNewScenario().setGameLength(window.testGameLength);
	}
	window.alertStrategy = curStrategy;

	switch (curStrategy) {
	    case FPA:
	    case GPA:
            alertmsg = 
	        writeDialogMsg("Game " + window.currentGame, "In this game, you will get an alert as to when to perform maintenance. The screen will flash and a \"Maintenance recommended\" message will appear at the bottom of screen."
                + "<b>In this game it is most recommended to follow these maintenance alerts and to perform maintenance (by pressing \"enter\") when these alerts appear.</b>"); break;
	    case NPA:
	        writeDialogMsg("Game " + window.currentGame, "In this game, you will not get an alert as to when to perform maintenance."
                + "<b>In this game it is most recommended to perform maintenance (by pressing \"enter\") every 20 seconds.</b>"); break;
	}

	
	panelController.onStopFreeze();
	initParms();
}

function createStrategies() {
    if (window.gameOrder == undefined) {
        window.gameOrder = "F,G,N";
        console.log("gameOrder wasn't defined. set to default - " + window.gameOrder);
    }
    console.log("gameOrderString - " + window.gameOrder);
    codeArray = window.gameOrder.split(",");
    strategies = [];
    for (var i = 0; i < codeArray.length; i++) {
        switch (codeArray[i]) {
            case "F": strategies.push(FPA); break;
            case "G": strategies.push(GPA); break;
            case "N": strategies.push(NPA); break;
            default:
                strategies.push(NPA);
                console.log("invalid startegy code: " + codeArray[i]);
        }
    }
    strategies.unshift(heatingGame);
    console.log("", strategies);
    return strategies;
}

function getTrialController(isPreviewGame) {
    if (isPreviewGame) {
        return new TrialControllerImpl();
    } else {
        return new TrialControllerInterface();
    }
}

function init() {
    freezeShip = false;
    currentTime = 0;
    lastTime = 0;
	setGameType();
	gameTimer = getNewScenario().gameLength;
	
	game.init();
	if (window.currentGame > 1) {
		clearGraphicsFromPrevGame();
	}
}

function clearGraphicsFromPrevGame() {
    game.score.clear();
	stopFreezeSpaceship();
	setFeulInventory(getNewScenario().getMaxFeulInventory());
}

/**
 * Define an object to hold all our images for the game so images
 * are only ever created once. This type of object is known as a
 * singleton.
 */
var imageRepository = new function () {
	// Define images
	this.background = new Image();
	this.spaceship = new Image();
	this.bullet = new Image();
	this.feulship = new Image();
	this.feulstation = new Image();
	this.enemyBullet = new Image();
	this.frozenship = new Image();

	// Ensure all images have loaded before starting the game
	var numImages = 7;
	var numLoaded = 0;
	function imageLoaded() {
		numLoaded++;
		if (numLoaded === numImages) {
			window.init();
		}
	}
	this.background.onload = function () {
		imageLoaded();
	}
	this.feulship.onload = function () {
		imageLoaded();
	}
	this.spaceship.onload = function () {
		imageLoaded();
	}
	this.bullet.onload = function () {
		imageLoaded();
	}

	this.enemyBullet.onload = function () {
		imageLoaded();
	}

	this.feulstation.onload = function () {
		imageLoaded();
	}
	this.frozenship.onload = function () {
		imageLoaded();
	}

	// Set images src
	this.background.src = "imgs/bg.png";
	this.spaceship.src = "imgs/ship.png";
	this.bullet.src = "imgs/bullet.png";
	this.enemyBullet.src = "imgs/meteor3.png";
	this.feulship.src = "imgs/feulShip.png";
	this.feulstation.src = "imgs/feulStation.png";
	this.frozenship.src = "imgs/freezeship.png";
}

/**
 * Creates the Drawable object which will be the base class for
 * all drawable objects in the game. Sets up defualt variables
 * that all child objects will inherit, as well as the defualt
 * functions.
 */
function Drawable() {
	this.init = function (x, y, width, height) {
		// Defualt variables
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	this.speed = 0;
	this.canvasWidth = 0;
	this.canvasHeight = 0;
	this.collidableWith = "";
	this.isColliding = false;
	this.type = "";

	// Define abstract function to be implemented in child objects
	this.draw = function () { };
	this.move = function () { };
	this.setSpeed = function (speed) {
		this.speed = speed;
	};
	this.isCollidableWith = function (object) {
		return (this.collidableWith === object.type);
	};
}

function FeulShip() {

	this.refuelCost = getNewScenario().getCostTable().getCost(enumAction.REFULING_BY_FUEL_SHIP);
	this.collidableWith = "";
	this.type = "feulShip";
	this.init = function (x, y, width, height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	};
	this.gaveFeul = false;
	this.alive = true;
	this.speedX = 3;
	this.speedY = 3;
	this.draw = function () {
		this.context.drawImage(imageRepository.feulship, this.x, this.y);
		freezeShip = true;
	};
	this.clear = function () {
		this.context.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);
		this.x = 0;
		this.y = 0;
		this.alive = false;
		this.gaveFeul = false;
	};
	this.move = function () {
		this.context.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);
		this.x = this.x + this.speedX >= game.ship.x - 30 ? this.x : this.x + this.speedX;
		this.y = this.y + this.speedY >= game.ship.y - 30 ? this.y : this.y + this.speedY;
		this.draw();
		if (this.x + 50 >= game.ship.x && this.y + 50 >= game.ship.y && !this.gaveFeul) {
			feulFillBySpaceship();
			game.score.alive = true;

			game.faulFill.get();
			game.score.init(this.x + 2, this.y + 2, this.refuelCost, false);
		
			this.gaveFeul = true;
			this.clear();
		}

	};
}
FeulShip.prototype = new Drawable();

function FeulStation(speed) {
	
	this.collidableWith = "ship";
	this.type = "feulstation";
	this.init = function (x, y, width, height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	};
	this.alive = false;
	this.isColliding = false;
	this.speed = speed;

	this.draw = function () {
		this.context.drawImage(imageRepository.feulstation, this.x, this.y);
	};

	this.move = function () {
		if (this.isColliding) {
		    game.faulFill.get();
		    var fuelCost = feulFillDone();
			game.score.init(this.x + 2, this.y + 2, fuelCost, false, currentTime);
			this.alive = false;
			this.isColliding = false;

			this.context.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);
			this.x = -this.width;
			this.y = -this.height;

		} else if (this.y >= Background.prototype.canvasHeight) {
			this.alive = false;
			this.context.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);
		} else if (this.y < Background.prototype.canvasHeight) {
			this.context.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);
			this.y += this.speed;
			this.alive = true;
			this.draw();
		}
	};

	this.clearIfAlive = function () {
		if (this.alive = false) {
			this.context.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);
		}
	};
}
FeulStation.prototype = new Drawable();




/**
 * Creates the Background object which will become a child of
 * the Drawable object. The background is drawn on the "background"
 * canvas and creates the illusion of moving by panning the image.
 */
function Background(speed) {
	this.speed = speed; // Redefine speed of the background for panning
	this.setSpeed = function (speed) {
		this.speed = speed;
	};
	// Implement abstract function
	this.draw = function () {
		// Pan background
		this.y += this.speed;
		//this.context.clearRect(0,0, this.canvasWidth, this.canvasHeight);
		this.context.drawImage(imageRepository.background, this.x, this.y);

		// Draw another image at the top edge of the first image
		this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight);

		// If the image scrolled off the screen, reset
		if (this.y >= this.canvasHeight)
			this.y = 0;
	};
}
//Set Background to inherit properties from Drawable
Background.prototype = new Drawable();


function FlashBackGround() {

    this.isFlash = false;
    this.startTime = 0;
    this.duration = 600;
    this.speed = 100;

    this.flash = function (time) {
        this.isFlash = true;
        this.startTime = time;
    }

    this.draw = function (time) {
        if (this.isFlash == true) {
            var dt = time - this.startTime;
            this.context.clearRect(0, 0, 900, 600);
            if (dt < this.duration) {
                state = Math.round(dt / this.speed) % 2;
                
                if (state == 0) {
                    this.context.fillStyle = "rgba(255, 255, 255, 0.0)";
                } else {
                    this.context.fillStyle = "rgba(255, 255, 255, 0.4)";
                }
                this.context.fillRect(0, 0, 900, 600);
            }
        }
    }

    this.stop = function () {
        this.isFlash = false;
        this.context.clearRect(0, 0, 900, 600);
    }
}


/**
 * Creates the Bullet object which the ship fires. The bullets are
 * drawn on the "main" canvas.
 */
function Bullet(object) {
	this.alive = false; // Is true if the bullet is currently in use
	var self = object;
	/*
	 * Sets the bullet values
	 */
	this.spawn = function (x, y, speed) {
		this.x = x;
		this.y = y;
		this.speed = speed;
		this.alive = true;
	};

	/*
	 * Uses a "drity rectangle" to erase the bullet and moves it.
	 * Returns true if the bullet moved of the screen, indicating that
	 * the bullet is ready to be cleared by the pool, otherwise draws
	 * the bullet.
	 */
	this.draw = function () {
		this.context.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);
		this.y -= this.speed;

		if (this.isColliding) {

			game.explosion.get();
			this.isColliding = false;

			return true;
		} else if (self === "bullet" && this.y <= 0 - this.height) {
			return true;
		} else if (self === "enemyBullet" && this.y >= this.canvasHeight) {
			return true;
		} else {
			if (self === "bullet") {
				this.context.drawImage(imageRepository.bullet, this.x, this.y);
			} else if (self === "enemyBullet") {
				this.context.drawImage(imageRepository.enemyBullet, this.x, this.y);
			}

			return false;
		}

	};

	/*
	 * Resets the bullet values
	 */
	this.clear = function () {
		this.x = 0;
		this.y = 0;
		this.speed = 0;
		this.alive = false;
		this.isColliding = false;
	};
}
Bullet.prototype = new Drawable();

/**
 * QuadTree object.
 *
 * The quadrant indexes are numbered as below:
 *     |
 *  1  |  0
 * ----+----
 *  2  |  3
 *     |
 */
function QuadTree(boundBox, lvl) {
	var maxObjects = 30;
	this.bounds = boundBox || {
		x: 0,
		y: 0,
		width: 0,
		height: 0
	};
	var objects = [];
	this.nodes = [];
	var level = lvl || 0;
	var maxLevels = 5;

	/*
	 * Clears the quadTree and all nodes of objects
	 */
	this.clear = function () {
		objects = [];

		for (var i = 0; i < this.nodes.length; i++) {
			this.nodes[i].clear();
		}

		this.nodes = [];
	};

	/*
	 * Get all objects in the quadTree
	 */
	this.getAllObjects = function (returnedObjects) {
		for (var i = 0; i < this.nodes.length; i++) {
			this.nodes[i].getAllObjects(returnedObjects);
		}

		for (var i = 0, len = objects.length; i < len; i++) {
			returnedObjects.push(objects[i]);
		}

		return returnedObjects;
	};

	/*
	 * Return all objects that the object could collide with
	 */
	this.findObjects = function (returnedObjects, obj) {
		if (typeof obj === "undefined") {
			//console.log("UNDEFINED OBJECT");
			return;
		}

		var index = this.getIndex(obj);
		if (index != -1 && this.nodes.length) {
			this.nodes[index].findObjects(returnedObjects, obj);
		}

		for (var i = 0, len = objects.length; i < len; i++) {
			returnedObjects.push(objects[i]);
		}

		return returnedObjects;
	};

	/*
	 * Insert the object into the quadTree. If the tree
	 * excedes the capacity, it will split and add all
	 * objects to their corresponding nodes.
	 */
	this.insert = function (obj) {
		if (typeof obj === "undefined") {
			return;
		}

		if (obj instanceof Array) {
			for (var i = 0, len = obj.length; i < len; i++) {
				this.insert(obj[i]);
			}

			return;
		}

		if (this.nodes.length) {
			var index = this.getIndex(obj);
			// Only add the object to a subnode if it can fit completely
			// within one
			if (index != -1) {
				this.nodes[index].insert(obj);

				return;
			}
		}

		objects.push(obj);

		// Prevent infinite splitting
		if (objects.length > maxObjects && level < maxLevels) {
			if (this.nodes[0] == null) {
				this.split();
			}

			var i = 0;
			while (i < objects.length) {

				var index = this.getIndex(objects[i]);
				if (index != -1) {
					this.nodes[index].insert((objects.splice(i, 1))[0]);
				} else {
					i++;
				}
			}
		}
	};

	/*
	 * Determine which node the object belongs to. -1 means
	 * object cannot completely fit within a node and is part
	 * of the current node
	 */
	this.getIndex = function (obj) {

		var index = -1;
		var verticalMidpoint = this.bounds.x + this.bounds.width / 2;
		var horizontalMidpoint = this.bounds.y + this.bounds.height / 2;

		// Object can fit completely within the top quadrant
		var topQuadrant = (obj.y < horizontalMidpoint && obj.y + obj.height < horizontalMidpoint);
		// Object can fit completely within the bottom quandrant
		var bottomQuadrant = (obj.y > horizontalMidpoint);

		// Object can fit completely within the left quadrants
		if (obj.x < verticalMidpoint &&
				obj.x + obj.width < verticalMidpoint) {
			if (topQuadrant) {
				index = 1;
			} else if (bottomQuadrant) {
				index = 2;
			}
		}
		// Object can fix completely within the right quandrants
		else if (obj.x > verticalMidpoint) {
			if (topQuadrant) {
				index = 0;
			} else if (bottomQuadrant) {
				index = 3;
			}
		}

		return index;
	};

	/*
	 * Splits the node into 4 subnodes
	 */
	this.split = function () {
		// Bitwise or [html5rocks]
		var subWidth = (this.bounds.width / 2) | 0;
		var subHeight = (this.bounds.height / 2) | 0;

		this.nodes[0] = new QuadTree({
			x: this.bounds.x + subWidth,
			y: this.bounds.y,
			width: subWidth,
			height: subHeight
		}, level + 1);
		this.nodes[1] = new QuadTree({
			x: this.bounds.x,
			y: this.bounds.y,
			width: subWidth,
			height: subHeight
		}, level + 1);
		this.nodes[2] = new QuadTree({
			x: this.bounds.x,
			y: this.bounds.y + subHeight,
			width: subWidth,
			height: subHeight
		}, level + 1);
		this.nodes[3] = new QuadTree({
			x: this.bounds.x + subWidth,
			y: this.bounds.y + subHeight,
			width: subWidth,
			height: subHeight
		}, level + 1);
	};
}

/**
 * Custom Pool object. Holds Bullet objects to be managed to prevent
 * garbage collection.
 * The pool works as follows:
 * - When the pool is initialized, it popoulates an array with
 *   Bullet objects.
 * - When the pool needs to create a new object for use, it looks at
 *   the last item in the array and checks to see if it is currently
 *   in use or not. If it is in use, the pool is full. If it is
 *   not in use, the pool "spawns" the last item in the array and
 *   then pops it from the end and pushed it back onto the front of
 *   the array. This makes the pool have free objects on the back
 *   and used objects in the front.
 * - When the pool animates its objects, it checks to see if the
 *   object is in use (no need to draw unused objects) and if it is,
 *   draws it. If the draw() function returns true, the object is
 *   ready to be cleaned so it "clears" the object and uses the
 *   array function splice() to remove the item from the array and
 *   pushes it to the back.
 * Doing this makes creating/destroying objects in the pool
 * constant.
 */
function Pool(maxSize) {
	this.size = maxSize; // Max bullets allowed in the pool
	var pool = [];
	this.setSize = function (newSize) {
		for (var i = 0; i < this.size; i++) {
			game.mainContext.clearRect(pool[i].x - 1, pool[i].y - 1, pool[i].width + 2, pool[i].height + 2);
			if (pool[i].alive == undefined) {
				pool[i].alive = false;

			}
		}
		this.size = newSize;
	};
	this.getPool = function () {
		var obj = [];
		for (var i = 0; i < this.size; i++) {
			if (pool[i].alive == undefined) {
				pool[i].alive = true;
			}
			if (pool[i].alive) {
				obj.push(pool[i]);
			}
		}
		return obj;
	};

	/*
	 * Populates the pool array with the given object
	 */
	 this.init = function (object) {
		if (object == "bullet") {
			for (var i = 0; i < this.size; i++) {
				// Initalize the object
				var bullet = new Bullet("bullet");
				bullet.init(0, 0, imageRepository.bullet.width,
						imageRepository.bullet.height);
				bullet.collidableWith = "enemyBullet";
				bullet.type = "bullet";
				pool[i] = bullet;
			}
		} else if (object == "enemyBullet") {
			for (var i = 0; i < this.size; i++) {
				var bullet = new Bullet("enemyBullet");
				bullet.init(0, 0, imageRepository.enemyBullet.width,
						imageRepository.enemyBullet.height);
				bullet.collidableWith = "ship";
				bullet.type = "enemyBullet";
				pool[i] = bullet;
			}
		}
	 };

	 /*
	  * Grabs the last item in the list and initializes it and
	  * pushes it to the front of the array.
	  */
	 this.get = function (x, y, speed) {
		 if (!pool[this.size - 1].alive) {
			 pool[this.size - 1].spawn(x, y, speed);
			 pool.unshift(pool.pop());
		 }
	 };

	 /*
	  * Used for the ship to be able to get two bullets at once. If
	  * only the get() function is used twice, the ship is able to
	  * fire and only have 1 bullet spawn instead of 2.
	  */
	 this.getTwo = function (x1, y1, speed1, x2, y2, speed2) {
		 if (!pool[this.size - 1].alive && !pool[this.size - 2].alive) {
			 this.get(x1, y1, speed1);
			 this.get(x2, y2, speed2);
		 }
	 };

	 this.shoot = function (mX, y, interval, numOfShot, speed2) {
	     var sX = mX - Math.round((numOfShot - 1) / 2) * interval;
	     if (numOfShot % 2 == 0) {
	         sX += Math.round(interval / 2);
	     }
	     for (var i = 0; i < numOfShot; i++) {
	         if (!pool[this.size - (1 + i)].alive) {
	             this.get(sX + i * interval, y, speed2);
             }   
	     }
	 };

	 /*
	  * Draws any in use Bullets. If a bullet goes off the screen,
	  * clears it and pushes it to the front of the array.
	  */
	 this.animate = function () {
		 for (var i = 0; i < this.size; i++) {
			 // Only draw until we find a bullet that is not alive
			 if (pool[i].alive) {
				 if (pool[i].draw()) {
					 pool[i].clear();
					 pool.push((pool.splice(i, 1))[0]);
				 }
			 } else
				 break;
		 }
	 };
}

/**
 * Create the Ship object that the player controls. The ship is
 * drawn on the "ship" canvas and uses dirty rectangles to move
 * around the screen.
 */
var shipSpeed = 13;
function Ship() {
	this.speed = shipSpeed;
	this.bulletPool = new Pool(60);
	this.fireRate = 15;
	this.counter = 0;
	this.collidableWith = "";
	this.type = "ship";

	this.init = function (x, y, width, height) {
		// Defualt variables
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		this.alive = true;
		this.isColliding = false;
		this.bulletPool.init("bullet");
	};

	this.draw = function () {
		this.context.drawImage(imageRepository.spaceship, this.x, this.y);
	};
	this.move = function () {

		this.counter++;
		// Determine if the action is move action
		if (KEY_STATUS.left || KEY_STATUS.right ||
				KEY_STATUS.down || KEY_STATUS.up) {
			// The ship moved, so erase it's current image so it can
			// be redrawn in it's new location
			this.context.clearRect(this.x - 0.5, this.y - 0.5, this.width + 1, this.height + 1);

			// Update x and y according to the direction to move and
			// redraw the ship. Change the else if's to if statements
			// to have diagonal movement.
			if (KEY_STATUS.left) {
				this.x -= this.speed;
				if (this.x <= 0) // Kep player within the screen
					this.x = 0;
			} else if (KEY_STATUS.right) {
				this.x += this.speed;
				if (this.x >= this.canvasWidth - this.width)
					this.x = this.canvasWidth - this.width;
			} else if (KEY_STATUS.up) {
				this.y -= this.speed;
				if (this.y <= 0)
					this.y = 0;
			} else if (KEY_STATUS.down) {
				this.y += this.speed;
				if (this.y >= this.canvasHeight - this.height)
					this.y = this.canvasHeight - this.height;
			}
		}
		// Redraw the ship
		if (this.isColliding) {

			if (!freezeShip) {

				this.isColliding = false;
				game.explosion.get();
			} else {

				this.isColliding = false;

			}
		}
		this.draw();

		if (KEY_STATUS.space && this.counter >= this.fireRate && !this.isColliding && !freezeShip) {
		    KEY_STATUS.space = false;
			this.fire();
			this.counter = 0;

		}
	};

	/*
	 * Fires two bullets
	 */
	this.fire = function () {

	    var interval = 25;
	    var mX = this.x + 35;
	    var numOfShot = 1;
	    
	    if (game.playerScore > 8000) {
	        numOfShot = 5;
	    } else if (game.playerScore > 4000) {
	        numOfShot = 4;
	    } else if (game.playerScore > 2000) {
	        numOfShot = 3;
	    } else if (game.playerScore > 500) {
	        numOfShot = 2;
	    }
	    this.bulletPool.shoot(mX, this.y, interval, numOfShot, 10);
		game.laser.get();
	};
}
Ship.prototype = new Drawable();

/**
 * Creates the Game object which will hold all objects and data for
 * the game.
 */
function Game() {
	/*
	 * Gets canvas information and context and sets up all game
	 * objects.
	 * Returns true if the canvas is supported and false if it
	 * is not. This is to stop the animation script from constantly
	 * running on browsers that do not support the canvas.
	 */
	this.init = function () {

		// Get the canvas elements
	    this.bgCanvas = document.getElementById('background');
	    this.flashBgCanvas = document.getElementById('flashBackground');
		this.shipCanvas = document.getElementById('ship');
		this.mainCanvas = document.getElementById('main');
		this.feulshipCanvas = document.getElementById('feulship');
		this.feulStationCanvas = document.getElementById('feulstation');
		this.scoreCanvas = document.getElementById('scoreCan');
		this.mechanicalScoreCanvas = document.getElementById('mechanicalScoreCanvas');
		// Test to see if canvas is supported. Only need to
		// check one canvas
		if (this.bgCanvas.getContext) {
		    this.bgContext = this.bgCanvas.getContext('2d');
		    this.flashBgContext = this.flashBgCanvas.getContext('2d');
			this.shipContext = this.shipCanvas.getContext('2d');
			this.mainContext = this.mainCanvas.getContext('2d');
			this.feulshipContext = this.feulshipCanvas.getContext('2d');
			this.feulStationContext = this.feulStationCanvas.getContext('2d');
			this.scoreContext = this.scoreCanvas.getContext('2d');
			this.mechanicalScoreContext = this.mechanicalScoreCanvas.getContext('2d');
			// Initialize objects to contain their context and canvas
			// information
			Background.prototype.context = this.bgContext;
			Background.prototype.canvasWidth = this.bgCanvas.width;
			Background.prototype.canvasHeight = this.bgCanvas.height;

			FlashBackGround.prototype.context = this.flashBgContext;
			FlashBackGround.prototype.canvasWidth = this.flashBgContext.width;
			FlashBackGround.prototype.canvasHeight = this.flashBgContext.height;

			Ship.prototype.context = this.shipContext;
			Ship.prototype.canvasWidth = this.shipCanvas.width;
			Ship.prototype.canvasHeight = this.shipCanvas.height;

			Bullet.prototype.context = this.mainContext;
			Bullet.prototype.canvasWidth = this.mainCanvas.width;
			Bullet.prototype.canvasHeight = this.mainCanvas.height;

			FeulShip.prototype.context = this.feulshipContext;
			FeulShip.prototype.canvasWidth = this.feulshipCanvas.width;
			FeulShip.prototype.canvasHeight = this.feulshipCanvas.height;

			FeulStation.prototype.context = this.feulStationContext;
			FeulStation.prototype.canvasWidth = this.feulStationCanvas.width;
			FeulStation.prototype.canvasHeight = this.feulStationCanvas.height;

			Score.prototype.canvasWidth = this.scoreCanvas.width;
			Score.prototype.canvasHeight = this.scoreCanvas.height;

			// Initialize the background object
			this.background = new Background(1);
			this.background.init(0, 0); // Set draw point to 0,0


			this.flashBackground = new FlashBackGround();

			// Initialize the ship object
			this.ship = new Ship();
			// Set the ship to start near the bottom middle of the canvas
			this.shipStartX = this.shipCanvas.width / 2 - imageRepository.spaceship.width;
			this.shipStartY = this.shipCanvas.height / 4 * 3 + imageRepository.spaceship.height;
			this.ship.init(this.shipStartX, this.shipStartY,
					imageRepository.spaceship.width, imageRepository.spaceship.height);

			this.feulstation = new FeulStation(0);
			this.feulstation.alive = false;

			this.score = new Score(14, this.scoreContext);
			this.score.alive = false;

			this.maintenanceOrRepairScore = new Score(14, this.mechanicalScoreContext);
			this.maintenanceOrRepairScore.alive = false;

			this.feulship = new FeulShip();
			this.feulship.alive = false;

			this.enemyBulletPool = new Pool(10);
			this.enemyBulletPool.init("enemyBullet");
			this.feulStatus = getNewScenario().getMaxFeulInventory();
			// Start QuadTree
			this.quadTree = new QuadTree({
				x: 0,
				y: 0,
				width: this.mainCanvas.width,
				height: this.mainCanvas.height
			});

			this.playerScore = 0;

			// Audio files
			this.laser = new SoundPool(10);
			this.laser.init("laser");

			this.explosion = new SoundPool(20);
			this.explosion.init("explosion");

			this.faulFill = new SoundPool(5);
			this.faulFill.init("faulFill");
			//	this.backgroundAudio = new Audio("sounds/kick_shock.wav");
			//	this.backgroundAudio.loop = true;
			//	this.backgroundAudio.volume = .25;
			//	this.backgroundAudio.load();

			this.gameOverAudio = new Audio("sounds/game_over.wav");
			this.gameOverAudio.loop = true;
			this.gameOverAudio.volume = .25;
			this.gameOverAudio.load();

			this.checkAudio = document.getElementById('loading').style.display = "none";
			game.start();


		}
	};

	// Start the animation loop
	this.start = function () {
		this.ship.draw();
		//	this.backgroundAudio.play();
		timer.resetStopwatch();
		gameTimer = getNewScenario().gameLength;
		animate();
		this.feulStatus = getNewScenario().getMaxFeulInventory();
		if (!pauseGame) {
			timer.Timer.play();
			var data = { assignmentId: window.assignmentId };

			$.ajax({
				type: "POST",
				url: "../MGGameService.svc/firstData/",
				data: JSON.stringify(data),
				dataType: 'json'
			});
		}

	};

	// Restart the game
	this.restart = function () {
		this.gameOverAudio.pause();
		game.ship.alive = true;
		document.getElementById('game-over').style.display = "none";
		this.bgContext.clearRect(0, 0, this.bgCanvas.width, this.bgCanvas.height);
		this.shipContext.clearRect(0, 0, this.shipCanvas.width, this.shipCanvas.height);
		this.feulStationContext.clearRect(0, 0, this.feulStationCanvas.width, this.feulStationCanvas.height);
		this.mainContext.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
		this.feulshipContext.clearRect(0, 0, this.feulshipCanvas.width, this.feulshipCanvas.height);

		this.quadTree.clear();

		this.background.init(0, 0);
		this.ship.init(this.shipStartX, this.shipStartY,
				imageRepository.spaceship.width, imageRepository.spaceship.height);
	};

	// Game over
	this.gameOver = function () {
		//	this.backgroundAudio.pause();
		//this.gameOverAudio.currentTime = 0;
		//this.gameOverAudio.play();
		onGameFinish();
		document.getElementById('game-over').style.display = "block";
		$("#game-summery").html(getGameSummery());
		$('#endScore').html("Total Points: " + totalScoreFromAllGames);
		timer.Timer.pause();
		this.ship.alive = false;


		//check if need more games
		if (currentGame < totalGames) {
			//window.location = "index.html?turkSubmitTo=" + turkSubmitTo + "&assignmentId=" + window.assignmentId;
			setTimeout(function () {
				game.restart();
				init();
			}, 3000);
		}
		else
			setTimeout(function () {
				window.location = "lastForm.html?turkSubmitTo=" + window.turkSubmitTo + "&assignmentId=" + window.assignmentId + "&gamesOrder=" + window.gameOrder;
			}, 3000);
	};
}

function getGameSummery() {
    if (window.isPreviewGame == true) {
		return "Be ready to play the real games";
	}

	if (currentGame == totalGames) {
		return "You played all games";
	}

	return "You played (" + currentGame + "/" + totalGames + ") games.";
}

/**
 * Ensure the game sound has loaded before starting the game
 */
function checkReadyState() {
	if (game.gameOverAudio.readyState === 4) { //&& game.backgroundAudio.readyState === 4) {
		window.clearInterval(game.checkAudio);
		document.getElementById('loading').style.display = "none";
		game.start();
	}
}

/**
 * A sound pool to use for the sound effects
 */
function SoundPool(maxSize) {
	var size = maxSize; // Max bullets allowed in the pool
	var pool = [];
	this.pool = pool;
	var currSound = 0;

	/*
	 * Populates the pool array with the given object
	 */
	this.init = function (object) {
		if (object == "laser") {
			for (var i = 0; i < size; i++) {
				// Initalize the object
				laser = new Audio("sounds/laser.wav");
				laser.volume = .12;
				laser.load();
				pool[i] = laser;
			}
		} else if (object == "explosion") {
			for (var i = 0; i < size; i++) {
				var explosion = new Audio("sounds/explosion.wav");
				explosion.volume = .1;
				explosion.load();
				pool[i] = explosion;
			}
		}
		else if (object == "faulFill") {
			for (var i = 0; i < size; i++) {
				var explosion = new Audio("sounds/faul.mp3");
				explosion.volume = .1;
				explosion.load();
				pool[i] = explosion;
			}
		}
	};

	/*
	 * Plays a sound
	 */
	this.get = function () {
		if (pool[currSound].currentTime == 0 || pool[currSound].ended) {
			pool[currSound].play();
		}
		currSound = (currSound + 1) % size;
	};
}

/**
 * The animation loop. Calls the requestAnimationFrame shim to
 * optimize the game loop and draws all game objects. This
 * function must be a gobal function and cannot be within an
 * object.
 */



var counter = 0;

var freezeShipCounter = 0;
var freezeShip = false;
var gameTimer = 0;
var feulShipCounter = 0;
var waitForFeulShip = false;
var lastTime = 0;
var feulChartValue = 100;
var currentTime;
var pauseGame = false;

var titleX = 0;
var titleY = 0;

function animate() {
	if (pauseGame)
		return;

	currentTime = window.currentTime1;

	if (gameTimer <= currentTime) {
		clearGraphicsFromPrevGame();
		game.gameOver();
	}

	$("#score").html(game.playerScore);
	insertToQuadtree();
	detectCollision();
	if (freezeShip && currentTime >= freezeShipCounter) {
		stopFreezeSpaceship();
	} else if (freezeShip && currentTime <= freezeShipCounter) {
		$("#freezeSec").html(freezeShipCounter - currentTime);
	}

	// Animate game objects
	if (game.ship.alive) {
		requestAnimFrame(animate);
		controllerLoop(currentTime);
		game.maintenanceOrRepairScore.draw(currentTime);
		game.score.draw(currentTime);

		if (!freezeShip) {
		    game.background.draw();
		    game.flashBackground.draw(currentTime);
			game.ship.move();
		} else {
			game.ship.context.drawImage(imageRepository.frozenship, game.ship.x, game.ship.y);
		}

		if (currentSpeed == 0) {
			game.ship.context.drawImage(imageRepository.frozenship, game.ship.x, game.ship.y);
			game.ship.context.fillStyle = "yellow";
			game.ship.context.font = "20px Arial";
			titleX = game.ship.x; titleY = game.ship.y - 30;
			//game.ship.context.fillText("Ship is frozen because of severe malfunction ",  titleX, titleY);
		}

		if (!game.feulship.alive && !freezeShip && game.feulStatus >= 0) {
			if ((lastTime + 1000) < currentTime) {
				//game.feulStatus -= 1000;
				lastTime = currentTime;
			}
			var feulStatusCalc = Math.floor(game.feulStatus / (getNewScenario().getMaxFeulInventory() / 100));
			if (feulChartValue - feulStatusCalc >= 1) {
				feulChartValue = feulStatusCalc;
				setFeulChart(feulChartValue);
			}
		}

		if (game.feulstation.alive) {
			game.feulstation.move();

		}

		game.ship.bulletPool.animate();
		game.enemyBulletPool.animate();
		var x = Math.floor(Math.random() * (Background.prototype.canvasWidth - 2 * imageRepository.enemyBullet.width - 20) / imageRepository.enemyBullet.width) * imageRepository.enemyBullet.width + imageRepository.enemyBullet.width + 10;
		var y = -Math.floor(Math.random() * 10) * imageRepository.enemyBullet.height;
		while (!chackEnemyBulletCollision(y, x)) {
			y = -Math.floor(Math.random() * 10) * imageRepository.enemyBullet.height;

		}
		game.enemyBulletPool.get(x, y, -6);

		panelController.draw(window.currentTime1);
	}

}

function freezeSpaceship(time) {
    onSpaceshipFreeze(currentTime, time);
	freezeShip = true;
	panelController.onFreeze();
	freezeShipCounter = currentTime + time;
	game.ship.context.drawImage(imageRepository.frozenship, game.ship.x, game.ship.y);
	game.ship.setSpeed(0);
	getNewScenario().shiftSamplers(time);
}

function stopFreezeSpaceship() {
	freezeShip = false;
	panelController.onStopFreeze();
	game.ship.setSpeed(currentSpeed);
	game.ship.context.clearRect(game.ship.x, game.ship.y, game.ship.width, game.ship.height);
}

function increaseScore(score) {
    game.playerScore += score;
    totalScoreFromAllGames += score;
}


function setFeulInventory(feulInvetory) {
	game.feulStatus = feulInvetory;
	feulChartValue = 100;
	setFeulChart(feulChartValue);
}

function setShootingBreakPercent(precent) {
	game.ship.fireRate = precent;
}
function detectCollision() {
	if (!freezeShip) {
		var objects = [];
		game.quadTree.getAllObjects(objects);

		for (var x = 0, len = objects.length; x < len; x++) {
			game.quadTree.findObjects(obj = [], objects[x]);

			for (y = 0, length = obj.length; y < length; y++) {

				// DETECT COLLISION ALGORITHM
				if (objects[x].collidableWith === obj[y].type &&
						(objects[x].x < obj[y].x + obj[y].width &&
								objects[x].x + objects[x].width > obj[y].x &&
								objects[x].y < obj[y].y + obj[y].height &&
								objects[x].y + objects[y].height > obj[y].y)) {
					if (objects[x].type == "feulship") {
						obj[y].isColliding = false;
					} else {
						obj[y].isColliding = true;
					}
					if (objects[x].type == "enemyBullet" && objects[y].type == "ship") {
						onMeteorCollision();
						hitScore = getNewScenario().hitByMeteorScore;
						increaseScore(-hitScore);
						game.score.init(objects[x].x + 2, objects[x].y + 2, hitScore, false, currentTime);
					} else if (objects[y].type == "enemyBullet" && objects[x].type == "bullet") {
						onMeteorShootDown();
						shutScore = getNewScenario().meteorScore;
						increaseScore(shutScore);
						game.score.init(objects[y].x + 2, objects[y].y + 2,shutScore, true, currentTime);
					}
					objects[x].isColliding = true;
				}
			}
		}
	}
};

function Score(size, context) {
	this.x = 0;
	this.y = 0;
	this.startTime = 0;
	this.duration = 2000;

	this.size = size;
	this.context = context;

	this.init = function (x, y, number, pos, time) {
	    this.clear();

	    this.startTime = time;
		this.x = x;
		this.y = y;
		this.number = number;
		this.positive = pos;
		this.text = this.positive ? "+" + this.number : "-" + this.number;
		this.size = size;
		this.color = this.positive ? "rgba(0,255,0," : "rgba(255,0,0,";
		this.alive = true;
		this.speed = -2;
		this.fontSpeed = 1;

		this.move();
	};

	this.draw = function (time) {
	    var dt = time - this.startTime;
	    if (this.alive == true && dt < this.duration) {
	        this.clear();
	        this.move();
	        this.context.font = this.size + 'pt Calibri';
	        this.context.fillStyle = this.color + (1-dt/this.duration) + ")";
	        this.context.fillText(this.text, this.x, this.y);
	    } else {
	        this.clear();
	        this.alive = false;
	    }
	};
	this.clear = function () {
		this.context.clearRect(this.x - 2 * this.size, this.y - 2 * this.size, 6 * this.size, 7 * this.size);
	};

	this.move = function () {
		this.clear();
		this.x += this.speed;
		this.y += this.speed;
		this.size += this.fontSpeed;
	};
}


function chackEnemyBulletCollision(y, x) {
	var height = imageRepository.enemyBullet.height;
	for (var i = 0; i < game.enemyBulletPool.count; i++) {

		if (game.enemyBulletPool[i].alive && game.enemyBulletPool[i].x == x && game.enemyBulletPool[i].y < y + height && enemyBulletPool[i].y + height > y) {
			return false;
		}
	}
	return true;
}

//The keycodes that will be mapped when a user presses a button.
//Original code by Doug McInnes
KEY_CODES = {
        13: 'enter',
		32: 'space',
		37: 'left',
		38: 'up',
		39: 'right',
		40: 'down',
};

//Creates the array to hold the KEY_CODES and sets all their values
//to true. Checking true/flase is the quickest way to check status
//of a key press and which one was pressed when determining
//when to move and which direction.
KEY_STATUS = {};
for (code in KEY_CODES) {
	KEY_STATUS[KEY_CODES[code]] = false;
}

var fireKeyIsPressed = false;
/**
 * Sets up the document to listen to onkeydown events (fired when
 * any key on the keyboard is pressed down). When a key is pressed,
 * it sets the appropriate direction to true to let us know which
 * key it was.
 */
document.onkeydown = function (e) {
    if (pauseGame == true) {
        e.preventDefault();
        return;
    }
	// Firefox and opera use charCode instead of keyCode to
	// return which key was pressed.
	var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
	if (KEY_CODES[keyCode]) {
	    e.preventDefault();
	    if (keyCode == 32) {
	        if (fireKeyIsPressed) {
	            KEY_STATUS[KEY_CODES[keyCode]] = false;
	            return;
	        } else {
	            fireKeyIsPressed = true;
	            onFirePressed();
	        }
	    }
	    if (keyCode == 13 && freezeShip == false) {
	        maintenanceActionDone();
	    }
		KEY_STATUS[KEY_CODES[keyCode]] = true;
	}
};
/**
 * Sets up the document to listen to ownkeyup events (fired when
 * any key on the keyboard is released). When a key is released,
 * it sets teh appropriate direction to false to let us know which
 * key it was.
 */
document.onkeyup = function (e) {
    if (pauseGame == true) {
        e.preventDefault();
        return;
    }
	var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
	if (KEY_CODES[keyCode]) {
		e.preventDefault();
		KEY_STATUS[KEY_CODES[keyCode]] = false;
	}

	if (keyCode == 32) {
	    fireKeyIsPressed = false;
    }
};

function insertToQuadtree() {
	game.quadTree.clear();
	game.quadTree.insert(game.ship);
	game.quadTree.insert(game.ship.bulletPool.getPool());
	game.quadTree.insert(game.enemyBulletPool.getPool());
	game.quadTree.insert(game.feulstation);
}

function makeAnewFeulStation(x) {
	game.feulstation.context.clearRect(game.feulstation.x - 1, game.feulstation.y - 1, game.feulstation.width + 2, game.feulstation.height + 2);
	var xFeul = x;
	var yFeul = -imageRepository.feulstation.height;
	game.feulstation.init(xFeul, yFeul, imageRepository.feulstation.width, imageRepository.feulstation.height);
	game.feulstation.alive = true;
	game.feulstation.move();

}
function callToFeulSpaceship(time) {
	if (!game.feulship.alive && !waitForFeulShip) {
		panelController.onWaitForFeulShip(time);
		game.feulship.context.clearRect(game.feulship.x - 1, game.feulship.y - 1, game.feulship.width + 2, game.feulship.height + 2);
		feulShipCounter = currentTime + time - getNewScenario().getCostTable().getTime(enumAction.REFULING_BY_FUEL_SHIP);
		freezeSpaceship(time);
		game.feulship.init(0, 0, imageRepository.feulship.width, imageRepository.feulship.height);
		game.feulship.draw();
		waitForFeulShip = true;

	}
	if (currentTime >= feulShipCounter) {
		waitForFeulShip = false;
		game.feulship.alive = true;
		game.feulship.move();
	}

}

function changeMechanicalCondition(newStatus) {
	if (newStatus != enumMC.FIX) {
		panelController.onMalfunctionOccur(newStatus);
	}
	changeShooting = false;
	changeSpeed = false;
}
/**
 * requestAnim shim layer by Paul Irish
 * Finds the first API that works to optimize the animation loop,
 * otherwise defaults to setTimeout().
 */
window.requestAnimFrame = (function () {
	var ret = window.requestAnimationFrame ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame ||
	window.oRequestAnimationFrame ||
	window.msRequestAnimationFrame ||
	function (/* function */
			callback, /* DOMElement */
			element) {
		window.setTimeout(callback, 1000 / 60);
	};
	return ret;
})();

function writeDialogMsg (title, msg) {
    $("#dialog-message").html(msg);
    $("#dialog-message").dialog({
        modal: false,
        title: title,
        width: 600,
        closeOnEscape: false,
        open: function(event, ui) { $(".ui-dialog-titlebar-close").hide(); },
        position: { my: "center", at: "center", of: $("#main") },
        buttons: [{
            text: "Ok",
            closeOnEscape: false,
            click: function () {
                $(this).dialog("close");
                pauseGame = false;
                timer.Timer.play();
                animate();
            },
            open: function () {
                $(".ui-dialog-titlebar-close").hide();
            },
        }]
    });
    $(".ui-dialog :button").blur();
    pauseGame = true;
    timer.Timer.pause();
}
